package cn.xiaolang.function.connection;


import lombok.extern.slf4j.Slf4j;

import java.sql.*;

/**
 * PS: 因为SQL注入只能对编译过程起作用
 * Mybatis怎么预防SQL注入？
 * 1. #将传入的数据都当成一个字符串，会对自动传入的数据加一个双引号。相当于JDBC中的PreparedStatement
 * 2. $将传入的数据直接显示生成在sql中
 *
 * @author Dean
 * @date 2022/9/28
 */
@Slf4j
public class SQLInjectionDemo {

    private static Connection conn;

    static {
        String driver = "com.mysql.cj.jdbc.Driver";
        String url = "jdbc:mysql://47.251.8.70:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&zeroDateTimeBehavior=convertToNull";
        String user = "iot";
        String password = "jjker1314";
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
        } catch (ClassNotFoundException e) {
            //数据库驱动类异常处理
            System.out.println("Sorry,can`t find the Driver!");
            e.printStackTrace();
        } catch (SQLException e) {
            //数据库连接失败异常处理
            e.printStackTrace();
        }

    }

    public static void main(String[] args) throws SQLException {
        // 1. 预编译，预防SQL注入：在执行sql前先把sql编译好
        // PS: 更重要的预防SQL注入攻击的措施是访问数据使用最小化权限。
        String preCompileSql = "select * from tb_push_dict where id= ?";
        String sql = "select * from tb_push_dict where id=";
        extractBySql("1", preCompileSql, true);
        extractBySql("1' or 1=1", preCompileSql, true);
        // 2. SQL拼接存在注入风险
        extractBySql(null, sql + "1 or 1=1", false);
    }

    private static void extractBySql(String userId, String sql, boolean isPreCompile) throws SQLException {
        PreparedStatement pstmt = conn.prepareStatement(sql);
        if (isPreCompile) {
            pstmt.setString(1, userId);
        }
        ResultSet rs = pstmt.executeQuery();
        while (rs.next()) {
            //按列的序号获取数据，通过从左往右数，第一列从1开始计数 int String
            Integer id = rs.getInt(1);//此时光标停留在第一行，获取数据库表中第一行第4列的数据
            String type = rs.getString(2);//此时光标停留在第一行，获取数据库表中第一行第4列的数据
            String name = rs.getString(3);//此时光标停留在第一行，获取数据库表中第一行第4列的数据
            String remark = rs.getString(4);//此时光标停留在第一行，获取数据库表中第一行第4列的数据
            log.info("id={},type={},name={},remark={}", id, type, name, remark);
        }
    }

}
